/*
 * TownEvent.cpp
 *
 * Created on: Sep 13, 2013
 *     Author: jason
 */
 
#include <iostream>
#include "../../../include/Common.hpp"
#include "../../../include/GameModes/TownMode/TownEvent.hpp"
#include "../../../include/GameModes/TownMode/TownMode.hpp"
#include "../../../include/GameModes/Dialogue.hpp"
#include "../../../include/GameModes/StateMachine.hpp"
 
namespace OForce_Mode {

CharacterEvent::CharacterEvent(std::string id, EVENT_TYPE type,
	 OForce_Object::Character *character) : TownEvent(id, type),
			 mCharacter(character)
{
	if (character == NULL) {
		std::cerr << "Error: passed an invalid character to event with ID:";
		std::cerr << id << ".\n";
	}
}

CharacterEvent::CharacterEvent(std::string id, EVENT_TYPE type, int characterID) :
		TownEvent(id, type)
{
	mCharacter = TownMode::getCurrentInstance()->getObjectManager()->getCharacter(characterID);
	if (mCharacter == NULL) {
		std::cerr << "Error getting character for event: " << id << std::endl;
	}
}
 
void CharacterEvent::start()
{
	if (mCharacter == NULL) {
		std::cerr << "Error: no character registered to this event.\n";
		return;
	}
	EventManager *ev = TownMode::getCurrentInstance()->getEventManager();
	if (mCharacter->controlledByEvent && ev) {
		ev->end(mCharacter->controlledByEvent, false);
	}
	mCharacter->acquireEvent(this);
}

void CharacterEvent::end()
{
	if (mCharacter && mCharacter->controlledByEvent) {
		mCharacter->releaseEvent(this);
	}
}

ScriptedCharacterEvent::ScriptedCharacterEvent(std::string id,
		OForce_Object::Character *character, std::string startFunc, std::string updateFunc) :
				CharacterEvent(id, EVENT_TYPE::SCRIPTED_CHARACTER_EVENT, character)
{
	LuaScript script = TownMode::getCurrentInstance()->getTownScript();
	TownMode::getCurrentInstance()->openMapTableSpace(true);
	script.openTable("map_function");
	if (!startFunc.empty()) {
		startFunction = script.ReadFunctionPointer(startFunc);
	}
	if (!updateFunc.empty()) {
		updateFunction = script.ReadFunctionPointer(updateFunc);
	}
	script.closeTable(); // map_function
	script.closeTable(); // tablespace
}

LookAtCharacterEvent::LookAtCharacterEvent(std::string id,
		OForce_Object::Character *character, OForce_Object::Object *otherObj) :
				CharacterEvent(id, EVENT_TYPE::LOOK_AT_EVENT, character),
				x(0.0),
				y(0.0),
				objectToLookAt(otherObj)
{
	if (otherObj == NULL) {
		std::cerr << "Cannot look at other object.\n";
	}
}

LookAtCharacterEvent::LookAtCharacterEvent(std::string id, int charID, int otherID) :
		CharacterEvent(id, EVENT_TYPE::LOOK_AT_EVENT, charID),
		x(-1.0),
		y(-1.0),
		objectToLookAt(NULL)
{

	objectToLookAt = TownMode::getCurrentInstance()->getObjectManager()->getObject(otherID);
	if (objectToLookAt == NULL) {
		std::cerr << "Cannot look at other object.\n";
	}
}

void LookAtCharacterEvent::start()
{
	CharacterEvent::start();
	if (objectToLookAt) {
		x = objectToLookAt->sprite.getPosition().x;
		y = objectToLookAt->sprite.getPosition().y;
	}
	if (x >= 0.0 && y >= 0.0) {
		mCharacter->lookAt(x, y);
	}
}

bool LookAtCharacterEvent::update()
{
	CharacterEvent::end();
	return true;
}

MoveCharacterEvent::MoveCharacterEvent(std::string id,
		OForce_Object::Character *character, float destX, float destY) :
				CharacterEvent(id, EVENT_TYPE::MOVE_EVENT, character),
				node(0),
				nodeX(0.0),
				nodeY(0.0),
				prevX(0.0),
				prevY(0.0),
				x(destX),
				y(destY)
{
}

MoveCharacterEvent::MoveCharacterEvent(std::string id,
		int charID, float destX, float destY) :
				CharacterEvent(id, EVENT_TYPE::MOVE_EVENT, charID),
				node(0),
				nodeX(0.0),
				nodeY(0.0),
				prevX(0.0),
				prevY(0.0),
				x(destX),
				y(destY)
{
}

void MoveCharacterEvent::setDestination(float destX, float destY)
{
	if (TownMode::getCurrentInstance()->getEventManager()->isEventActive(mId)) {
		std::cerr << "Error: attempted to modify path while event was ongoing.\n";
		std::cerr << mId << "\n";
		return;
	}
	x = destX;
	y = destY;
	mPath.clear();
	node = 0;
}

void MoveCharacterEvent::start()
{
	CharacterEvent::start();
	bool pathAvailable;
	prevX = mCharacter->getXPosition();
	prevY = mCharacter->getYPosition();
	sf::Vector2f dest (x, y);
	// verify that we are not yet at destination
	if ((int) x == (int)prevX && (int) y == (int) prevY) {
		return;
	}
	pathAvailable = TownMode::getCurrentInstance()->getObjectManager()->
			findPath(mCharacter, mPath, dest);
	node = 0;
	if (!pathAvailable) {
		std::cerr << "Cannot get to destination.\n";
		return;
	}
	nodeX = mPath[node].x;
	nodeY = mPath[node].y;
//	std::cout << mPath[0].x << " " << mPath[0].y << " ";
//	std::cout << node << " " <<  nodeX << " " << nodeY << " " << mPath[4].x << " " << mPath[4].y << "\n";
}

bool MoveCharacterEvent::update()
{
	if (mPath.empty()) {
		end();
		return true;
	}
	float myX = mCharacter->sprite.getPosition().x;
	float myY = mCharacter->sprite.getPosition().y;
	float distanceMoved = dt*mCharacter->getMoveSpeed();
	if (fabs(nodeX-myX) < distanceMoved && fabs(nodeY-myY) < distanceMoved) {
		node++;
		if (node < mPath.size()) {
			nodeX = mPath[node].x;
			nodeY = mPath[node].y;
		}
	} else if ((myX != prevX) || (myY != prevY)) {
		prevX = myX;
		prevY = myY;
	}
	if (OForce_Mode::TownMode::getCurrentInstance()->getCurrentState()
						!= OForce_Mode::DIALOGUE) {
		setDirection();
	}
	if (fabs(myX-x) < distanceMoved && fabs(myY-y) < distanceMoved) {
		end();
		return true;
	}
	return false;
}

void MoveCharacterEvent::end()
{
	CharacterEvent::end();
}

void MoveCharacterEvent::setDirection()
{
	float myX = mCharacter->sprite.getPosition().x;
	float myY = mCharacter->sprite.getPosition().y;
	float distanceMoved = dt*mCharacter->getMoveSpeed();
	if (myY - nodeY > distanceMoved) {
		mCharacter->setDirection(OForce_Object::UP);
	} else if (myY - nodeY < -distanceMoved) {
		mCharacter->setDirection(OForce_Object::DOWN);
	} else if (myX - nodeX > distanceMoved) {
		mCharacter->setDirection(OForce_Object::LEFT);
	} else if (myX - nodeX < -distanceMoved) {
		mCharacter->setDirection(OForce_Object::RIGHT);
	}
}

ScriptedCharacterEvent::ScriptedCharacterEvent(std::string id,
		int charID, std::string startFunc, std::string updateFunc) :
						CharacterEvent(id, EVENT_TYPE::SCRIPTED_CHARACTER_EVENT, charID)
{
	CharacterEvent::start();
	LuaScript script = TownMode::getCurrentInstance()->getTownScript();
	TownMode::getCurrentInstance()->openMapTableSpace(true);
	script.openTable("map_function");
	if (!startFunc.empty()) {
		startFunction = script.ReadFunctionPointer(startFunc);
	}
	if (!updateFunc.empty()) {
		updateFunction = script.ReadFunctionPointer(updateFunc);
	}
	script.closeTable(); // map_function
	script.closeTable(); // tablespace
}

void ScriptedCharacterEvent::start()
{
	CharacterEvent::start();
	if (startFunction.is_valid()) {
		luabind::call_function<void>(startFunction, mCharacter);
	}
}

bool ScriptedCharacterEvent::update()
{
	bool finished = false;
	if (updateFunction.is_valid()) {
		finished = luabind::call_function<bool>(updateFunction, mCharacter);
	} else {
		finished = true;
	}
	return finished;
}

AnimateCharacterEvent::AnimateCharacterEvent(std::string id, OForce_Object::Character *character,
		std::string animationName, float animationDuration) :
				CharacterEvent(id, OForce_Mode::ANIMATE_EVENT, character),
				mAnimName(animationName),
				timer(animationDuration),
				mCharacter(character)
{
}

AnimateCharacterEvent::AnimateCharacterEvent(std::string id, int charID,
		std::string animationName, float animationDuration) :
				CharacterEvent(id, OForce_Mode::ANIMATE_EVENT, charID),
				mAnimName(animationName),
				timer(animationDuration)
{
	mCharacter = OForce_Mode::TownMode::getCurrentInstance()->
			getObjectManager()->getCharacter(charID);
}

void AnimateCharacterEvent::start()
{
	CharacterEvent::start();
	if (mCharacter) {
		mCharacter->setCustomAnimation(mAnimName, timer);
	}
}

bool AnimateCharacterEvent::update()
{
	if (!mCharacter || mCharacter->isCustomAnimation()) {
		end();
		return true;
	}
	return false;
}

void AnimateCharacterEvent::end()
{
	if (mCharacter) {
		mCharacter->endCustomAnimation();
	}
	mCharacter = NULL;
	CharacterEvent::end();
}

DialogueEvent::DialogueEvent(std::string id, Dialogue *dialogue) :
		TownEvent(id, DIALOGUE_EVENT),
		mDialogueId(dialogue->getDialogueID())
{
}

DialogueEvent::DialogueEvent(std::string id, int dialogueID) :
		TownEvent(id, DIALOGUE_EVENT),
		mDialogueId(dialogueID)
{
}

void DialogueEvent::start()
{
	TownMode::getCurrentInstance()->getDialogueManager()->startDialogue(mDialogueId);
}

bool DialogueEvent::update()
{
	Dialogue *activeDialogue = TownMode::getCurrentInstance()->getDialogueManager()->
			getCurrentDialogue();
	if (activeDialogue && activeDialogue->getDialogueID() == mDialogueId) {
		return false;
	} else {
		return true;
	}
}

SoundEvent::SoundEvent(std::string id, std::string soundFile) :
		TownEvent(id, SOUND_EVENT)
{
	std::string path = "data/Sounds/";
	path.append(soundFile);
	if (!mSoundBuffer.loadFromFile(path)) {
		std::cerr << "Could not find " << path << std::endl;
		mSound.setBuffer(mSoundBuffer);
	}
}

bool SoundEvent::update()
{
	if (mSound.getStatus() != sf::Sound::Playing) {
		return true;
	}
	return false;
}

MapTransitionEvent::MapTransitionEvent(std::string id, std::string newMap,
		std::string origin, bool f) :
				TownEvent(id, MAP_TRANSITION),
				mId(id),
				mNewMap(newMap),
				mOldMap(origin),
				mFade(f),
				done(false)
{
}

void MapTransitionEvent::start()
{
	if (mFade) {
		TownMode::getCurrentInstance()->startFading();
	}
}

bool MapTransitionEvent::update()
{
	if (TownMode::getCurrentInstance()->isCurrentlyFading()) {
		return false;
	}
	if (!done) {
		std::cout << "Not done.\n";
		GlobalState::Instance()->setPreviousLocation(mOldMap);
		TownMode *town = new TownMode(mNewMap);
		StateMachine::Instance()->pop();
		StateMachine::Instance()->add(town);
//		StateMachine::Instance()->setCurrentState(town);
		done = true;
	}
	return true;
}

EventManager::~EventManager()
{
	for (std::unordered_map<std::string, TownEvent *>::iterator it = eventMap.begin();
			it != eventMap.end(); it++) {
		delete (*it).second;
	}
	activeEvents.clear();
	pausedEvents.clear();
	delayedEvents.clear();
	pausedDelayedEvents.clear();
	eventMap.clear();
}
 
 void EventManager::update()
 {
	// Vector for events that change state in this update pass.
	std::vector<TownEvent *> newEvents;
	std::vector<TownEvent *> finishedEvents;
	// update all timers
	for (std::vector<std::pair<float, TownEvent *> >::iterator it = delayedEvents.begin();
			it != delayedEvents.end(); ) {
		(*it).first -= dt;
		if ((*it).first <= 0) {
			// Event is now active.
			TownEvent *event = (*it).second;
			it = delayedEvents.erase(it);
			newEvents.push_back(event);
		} else {
			it++;
		}
	}
	// Start all new events.
	for (std::vector<TownEvent *>::iterator it = newEvents.begin();
			it != newEvents.end(); it++) {
		startEvent(*it);
	}
	updating = true;
	// update all events
	for (std::vector<TownEvent *>::iterator it = activeEvents.begin(); 
			it != activeEvents.end();) {
		if ((*it)->update()) {
			// Event finished
			finishedEvents.push_back(*it);
			it = activeEvents.erase(it);
		} else {
			it++;
		}
	}
	updating = false;
	// Examine finished events for links
	for (std::vector<TownEvent *>::iterator it = finishedEvents.begin();
			it != finishedEvents.end(); it++) {
		processLinks(*it, false);
	}
 }
 
 void EventManager::addEvent(TownEvent *event)
 {
	if (event == NULL) {
		std::cerr << "Error: cannot insert NULL event.\n"; 
		return;
	}
	if (getEvent(event->getID()) != NULL) {
		std::cerr << "Error: event with ID, " << event->getID() << ", already exists\n";
		return;
	}
	eventMap.insert(std::make_pair(event->getID(), event));
 }
 
 void EventManager::startEvent(std::string id)
 {
	TownEvent *event = getEvent(id);
	if (event == NULL) {
		std::cerr << "Error: could not start event with ID: " << id << "\n";
		std::cerr << "It does not exist.\n";
		return;
	}
	startEvent(event);
 }
 
 void EventManager::startEvent(std::string id, float delay)
 {
	TownEvent *event = getEvent(id);
	if (event == NULL) {
		std::cerr << "Error: could not start event with ID: " << id << "\n";
		std::cerr << "It does not exist.\n";
		return;
	}
	if (delay == 0) {
		startEvent(event);
	} else {
		delayedEvents.push_back(std::make_pair(delay, event));
	}
 }
 
 void EventManager::startEvent(TownEvent *event, float delay) 
 {
	if (event == NULL) {
		std::cerr << "Error: attempted to start NULL event.\n";
		return;
	}
	if (delay == 0) {
		startEvent(event);
	} else {
		delayedEvents.push_back(std::make_pair(delay, event));
	}
 }
 
 void EventManager::startEvent(TownEvent *event) 
 {
	if (event == NULL) {
		std::cerr << "Error: attempted to start NULL event.\n";
		return;
	}
	if (updating) {
		std::cerr << "Error: attempted to start event with ID, " << event->getID();
		std::cerr << ", while an another event was updating.\n";
		std::cerr << "Fix this map\'s script.\n";
		return;
	}
	for (std::vector<TownEvent *>::iterator it = activeEvents.begin();
			it != activeEvents.end(); it++) {
		if (*it == event) {
			std::cerr << "Error: The event, " << event->getID() << ", is already active.\n";
			std::cerr << "Please check your script.\n";
			return;
		}
	}
	activeEvents.push_back(event);
	event->start();
	processLinks(event, true);
 }
 
 void EventManager::pauseEvent(std::string id)
 {
	if (updating) {
		std::cerr << "Error: attempted to pause event, " << id << " while updating.\n";
		std::cerr << "Please fix your script.\n";
		return;
	}
	// Find event with id
	for (std::vector<TownEvent *>::iterator it = activeEvents.begin(); 
			it != activeEvents.end(); ) {
		if ((*it)->getID() == id) {
			pausedEvents.push_back(*it);
			it = activeEvents.erase(it);
		} else {
			it++;
		}
	}
 }
 
 void EventManager::resumeEvent(std::string id) 
 {
	if (updating) {
		std::cerr << "Error: attempted to resume event, " << id << " while updating.\n";
		std::cerr << "Please fix your script.\n";
		return;
	}
	// Find event with id
	for (std::vector<TownEvent *>::iterator it = pausedEvents.begin(); 
			it != pausedEvents.end();) {
		if ((*it)->getID() == id) {
			activeEvents.push_back(*it);
			it = pausedEvents.erase(it);
		} else {
			it++;
		}
	}
 }
 
 void EventManager::pauseAllEvents(OForce_Object::Character *character) 
 {
	if (character == NULL) {
		return;
	}
	if (updating) {
		std::cerr << "Error: attempted to pause all events for character: " << character->getID();
		std::cerr << ", while updating.\n Please fix your script.\n";
		return;
	}
	// Pause all active events
	for (std::vector<TownEvent *>::iterator it = activeEvents.begin();
			it != activeEvents.end();) {
		CharacterEvent *event = dynamic_cast<CharacterEvent *>(*it);
		if (event && event->getCharacter() == character) {
			pausedEvents.push_back(*it);
			it = activeEvents.erase(it);
		} else {
			it++;
		}
	}
	// Pause all delayed events
	for (std::vector<std::pair<float, TownEvent *> >::iterator it = delayedEvents.begin();
			it != delayedEvents.end();) {
		CharacterEvent *event = dynamic_cast<CharacterEvent *>((*it).second);
		if (event && event->getCharacter() == character) {
			pausedDelayedEvents.push_back(*it);
			it = delayedEvents.erase(it);
		} else {
			it++;
		}
	}
 }
 
 void EventManager::resumeAllEvents(OForce_Object::Character *character) 
 {
	if (character == NULL) {
		return;
	}
	if (updating) {
		std::cerr << "Error: attempted to resume all events for character: " << character->getID();
		std::cerr << ", while updating.\n Please fix your script.\n";
		return;
	}
	// Resume all active events
	for (std::vector<TownEvent *>::iterator it = pausedEvents.begin();
			it != pausedEvents.end();) {
		CharacterEvent *event = dynamic_cast<CharacterEvent *>(*it);
		if (event && event->getCharacter() == character) {
			activeEvents.push_back(*it);
			it = pausedEvents.erase(it);
		} else {
			it++;
		}
	}
	// Resume all delayed events
	for (std::vector<std::pair<float, TownEvent *> >::iterator it = pausedDelayedEvents.begin();
			it != pausedDelayedEvents.end();) {
		CharacterEvent *event = dynamic_cast<CharacterEvent *>((*it).second);
		if (event && event->getCharacter() == character) {
			delayedEvents.push_back(*it);
			it = pausedDelayedEvents.erase(it);
		} else {
			it++;
		}
	}
 }
 
 void EventManager::endAllEvents(OForce_Object::Character *character)
 {
	if (character == NULL) {
		return;
	}
	if (updating) {
		std::cerr << "Error: attempted to end all events for character: " << character->getID();
		std::cerr << ", while updating.\n Please fix your script.\n";
		return;
	}
	// End active events
	for (std::vector<TownEvent *>::iterator it = activeEvents.begin(); 
			it != activeEvents.end(); ) {
		CharacterEvent *event = dynamic_cast<CharacterEvent *>(*it);
		if (event && event->getCharacter() == character) {
			event->end();
			it = activeEvents.erase(it);
		} else {
			it++;
		}
	}
	// end delayed events
	for (std::vector<std::pair<float, TownEvent *> >::iterator it = delayedEvents.begin();
			it != delayedEvents.end(); ) {
		CharacterEvent *event = dynamic_cast<CharacterEvent *>((*it).second);
		if (event && event->getCharacter() == character) {
			event->end();
			it = delayedEvents.erase(it);
		} else {
			it++;
		}
	}
	// end paused events
	for (std::vector<TownEvent *>::iterator it = pausedEvents.begin(); 
			it != pausedEvents.end(); ) {
		CharacterEvent *event = dynamic_cast<CharacterEvent *>(*it);
		if (event && event->getCharacter() == character) {
			event->end();
			it = pausedEvents.erase(it);
		} else {
			it++;
		}
	}
	// end paused delayed events
	for (std::vector<std::pair<float, TownEvent *> >::iterator it = pausedDelayedEvents.begin();
			it != pausedDelayedEvents.end(); ) {
		CharacterEvent *event = dynamic_cast<CharacterEvent *>((*it).second);
		if (event && event->getCharacter() == character) {
			event->end();
			it = pausedDelayedEvents.erase(it);
		} else {
			it++;
		}
	}
 }
 
 void EventManager::end(std::string id, bool startLinks /* = true */)
 {
	if (updating) {
		std::cerr << "Error: attempted to end event, " << id << ", while updating.\n";
		std::cerr << "Please fix your script.\n";
		return;
	}
	std::cout << id << std::endl;
	// Check if ID matches an active event
	for (std::vector<TownEvent *>::iterator it = activeEvents.begin();
			it != activeEvents.end(); ) {
		if ((*it)->getID() == id) {
			// CharacterEvents need to end themselves
			CharacterEvent *charEvent = dynamic_cast<CharacterEvent *>(*it);
			if (charEvent) {
				charEvent->end();
			}
			TownEvent *event = *it;
			it = activeEvents.erase(it);
			if (startLinks) {
				processLinks(event, false);
			}
		} else {
			it++;
		}
	}
	// Check if ID matches a paused event
	for (std::vector<TownEvent *>::iterator it = pausedEvents.begin();
			it != pausedEvents.end(); ) {
		if ((*it)->getID() == id) {
			// CharacterEvents need to end themselves
			CharacterEvent *charEvent = dynamic_cast<CharacterEvent *>(*it);
			if (charEvent) {
				charEvent->end();
			}
			TownEvent *event = *it;
			it = pausedEvents.erase(it);
			if (startLinks) {
				processLinks(event, false);
			}
		} else {
			it++;
		}
	}
	// Check if ID matches delayed event
	for (std::vector<std::pair<float, TownEvent *> >::iterator it = delayedEvents.begin();
			it != delayedEvents.end(); ) {
		if ((*it).second->getID() == id) {
			// CharacterEvents need to end themselves
			CharacterEvent *charEvent = dynamic_cast<CharacterEvent *>((*it).second);
			if (charEvent) {
				charEvent->end();
			}
			TownEvent *event = (*it).second;
			it = delayedEvents.erase(it);
			if (startLinks) {
				processLinks(event, false);
			}
		} else {
			it++;
		}
	}
	// Check if ID matches paused delayed event
	for (std::vector<std::pair<float, TownEvent *> >::iterator it = pausedDelayedEvents.begin();
			it != pausedDelayedEvents.end(); ) {
		if ((*it).second->getID() == id) {
			// CharacterEvents need to end themselves
			CharacterEvent *charEvent = dynamic_cast<CharacterEvent *>((*it).second);
			if (charEvent) {
				charEvent->end();
			}
			TownEvent *event = (*it).second;
			it = pausedDelayedEvents.erase(it);
			if (startLinks) {
				processLinks(event, false);
			}
		} else {
			it++;
		}
	}
 }
 
 void EventManager::end(TownEvent *event, bool startLinks /* = true */)
 {
	if (event == NULL) {
		std::cerr << "Error: event does not exist!\n";
		return;
	}
	std::cout << event->getID() << "\n";
	if (updating) {
		std::cerr << "Error: attempted to end event, " << event->getID() << ", while updating.\n";
		std::cerr << "Please fix your script.\n";
		return;
	}
	end(event->getID(), startLinks);
 }
 
 bool EventManager::isEventActive(std::string id) 
 {
	for (std::vector<TownEvent *>::iterator it = activeEvents.begin();
			it != activeEvents.end(); it++) {
		if ((*it)->getID() == id) {
			return true;
		}
	}
	return false;
 }
 
 TownEvent *EventManager::getEvent(std::string id) 
 {
	std::unordered_map<std::string, TownEvent *>::iterator it = eventMap.find(id);
	if (it == eventMap.end()) {
		return NULL;
	} else {
		return it->second;
	}
 }
 
 void EventManager::processLinks(TownEvent *event, bool concurrent) 
 {
	for (int i = 0; i < event->vLink.size(); i++) {
		LinkEvent &link = event->vLink[i];
		// Check if link is set for this event time
		if (link.mConcurrent != concurrent) {
			continue;
		} else {
			startEvent(link.mId, link.timer);
		}
	}
 }
 } // namespace OForce_Mode
 
